<?php

namespace Aoe\Emulator\Controller;


use Aoe\Attributes\Controller\SetResponse;
use Aoe\Attributes\Controller\Trace;
use Aoe\Emulator\Ifc\Key;
use Aoe\Emulator\Ifc\Type;
use Aoe\Emulator\Ifc\Vu;
use Aoe\Emulator\Processor\Addition;
use Aoe\Emulator\Processor\Search;
use Aoe\Emulator\Processor\Texture;
use Aoe\Emulator\Ui\Browser;
use Aoe\Emulator\Ui\Context;
use Aoe\Emulator\Ui\Form;
use Aoe\Emulator\Ui\Table;
use Aoe\Emulator\Ui\Tree;
use Aoe\Intent\Render\Render;
use Aoe\Intent\Request\Http;
use Aoe\Intent\Request\Request;
use Aoe\Intent\User\Power;
use Aoe\Intent\User\User;
use Aoe\Util\Upload;
use Exception;
use Throwable;

/**
 * # Aoe-Api /api/... 自动引入
 *
 * @see Router
 */
#[Trace]
#[SetResponse(Spa::class)]
class Api extends Aoe
{
    const string ADD_OK       = '添加成功';
    const string EDIT_OK      = '已修改';
    const string DELETE_OK    = '已删除';
    const string SAVED        = '保存完成';
    const string AUTH_FBD     = '权限不足';
    
    const string PARAM_KEY    = 'tree';
    const string COMPILER_KRY = 'compiler';
    const string ROWS         = 'rows';
    const string TOTAL        = 'total';
    const string WHERE        = 'where';
    const string PAGINATION   = 'pagination';
    const string CURRENT      = 'current';
    const string SIZE         = 'pageSize';
    const string SORT         = 'sort';
    const string QUERY        = 'query';

    protected function add_action(Http $http): void
    {
        try {
            $this->valid_add_auth($http);
            strtolower($http->getMethod()) === Http::METHOD_POST
                ? $this->on_add_api($http)
                : $this->on_add_view($http);
        }catch (Exception $e){
            $http->response->error($e->getMessage());
        }
    }
    /**
     * @throws Exception
     */
    protected function valid_add_auth(Http $http): void
    {
        if (!$http->user->hasNormalPower($http->section->resource, Power::ADD))
            throw new Exception(self::AUTH_FBD);
    }
    /**
     * @throws Exception
     */
    protected function on_add_api(Http $http): void
    {
        $this->create_entity()->insert(
            $this->_add_column($http)
        );
        $http->response->ok(self::ADD_OK);
    }
    /**
     * @throws Exception
     */
    protected function on_add_view(Http $http): void
    {
        $http->response->complete([
            Vu::COMPONENT => Vu::FORM,
            Vu::SCHEMA    => new Form(new Context($this, $http))->render(),
        ]);
    }
    
    
    protected function edit_action(Http $http): void
    {
        try {
            $this->valid_edit_auth($http);
            strtolower($http->getMethod()) === Http::METHOD_POST
                ? $this->on_edit_api($http)
                : $this->on_edit_view($http);
        }catch (Exception $e){
            $http->response->error($e->getMessage());
        }
    }
    
    /**
     * @throws Exception
     */
    protected function valid_edit_auth(Http $http): void
    {
        $id = $http->getParam(Type::ID);
        if (!$http->user->hasPower($http->section->resource, Power::EDIT, $id))
            throw new Exception(self::AUTH_FBD);
    }
    
    /**
     * @throws Exception
     */
    protected function on_edit_api(Request $request): void
    {
        $id = $request->getParam(Type::ID);
        $this->create_entity()->setId($id)->modify($request->getParam());
        $request->response->ok(self::EDIT_OK);
    }
    
    /**
     * @throws Exception
     */
    protected function on_edit_view(Http $http): void
    {
        $http->response->complete([
            Vu::COMPONENT => Vu::FORM,
            Vu::SCHEMA    => new Form(new Context($this, $http))->render(),
        ]);
    }
    
    protected function row_action(Http $http): void
    {
        try {
            $id = $http->getParam(Type::ID);
            $this->valid_edit_auth($http);
            $elements = $this->get_elements($this->getViewDeclarer('edit'), self::FIELDS);
            $http->response->complete($this->create_search()->setId($id)->setElements($elements)->row());
        }catch (Throwable $e){
            $http->response->error($e->getMessage());
        }
    }
    
    
    protected function delete_action(Http $http): void
    {
        try {
            $id = $http->getParam(Type::ID);
            $this->valid_delete_auth($http, $id);
            $this->create_entity()->setId($id)->remove();
            $http->response->ok(self::DELETE_OK);
        }catch (Throwable $e){
            $http->response->error($e->getMessage());
        }
    }
    
    /**
     * @throws Exception
     */
    protected function valid_delete_auth(Http $http, int $id): void
    {
        if (!$http->user->hasPower($http->section->resource, Power::DEL, $id))
            throw new Exception(self::AUTH_FBD);
    }

    
    protected function list_action(Http $http): void
    {
        try {
            $http->response->complete([
                Vu::COMPONENT => Vu::TABLE,
                Vu::SCHEMA    => new Browser(new Context($this, $http))->render(),
            ]);
        }catch (Exception $e){
            $http->response->error($e->getMessage());
        }
    }
    
    protected function rows_action(Http $http): void
    {
        try {
            $condition = $this->_condition($http, $this->get_action_def($http));
            $search    = $this->create_search()->orWhere($this->_fill_conditions($http->user, $condition));
            
            $total      = $search->count();
            $pagination = $this->_addition($search, $http);
            
            $schema   = $this->getViewDeclarer($http);
            $elements = $this->get_elements($schema, self::COLUMNS);
            $values     = $search->setElements($elements)->rows();
            
            $return                                 = [];
            $return[self::ROWS]                     = array_values($values);
            $return[Table::PAGINATION]              = $pagination;
            $return[Table::PAGINATION][self::TOTAL] = $total;
            
            $http->response->complete($return);
        }catch (Throwable $e){
            $http->response->error($e->getMessage());
        }
        
    }
    
    protected function tree_action(Http $http): void
    {
        try {
            $http->response->complete([
                Vu::COMPONENT => Vu::TREE,
                Vu::SCHEMA    => new Tree(new Context($this, $http))->render(),
            ]);
        }catch (Exception $e){
            $http->response->error($e->getMessage());
        }
    }
    
    /**
     * 树结构加载
     *
     * @param Http $http
     *
     * @throws Exception
     * @noinspection PhpUnused
     */
    protected function load_action(Http $http): void
    {
        try {
            $http->response->complete(Texture::get($this->getModel())->load(
                $http->getParam(self::COMPILER_KRY),
            ));
        } catch (Throwable $e) {
            $http->response->complete(null, true, $e->getMessage());
        }
    } // 排序方向

    /**
     * 树结构保存
     *
     * @param Http $http
     *
     * @noinspection PhpUnused
     */
    protected function save_action(Http $http): void
    {
        try {
            Texture::get($this->getModel())->save($http->getParam(self::PARAM_KEY));
            $http->response->ok(self::SAVED);
        } catch (Throwable $e) {
            $http->response->error($e->getMessage());
        }
    }
    
    
    /**
     * ## 关联模型选项 Array<{ label: string, value: string | number }>
     *
     * @param Request $request
     *
     * @return void
     * @noinspection PhpUnused
     */
    protected function options_action(Request $request): void
    {
        $where = $request->getParam();
        try {
            $model = $this->getModel();
            $curd  = $this->create_search($model);
            if (!empty($where)) $curd->orWhere($where);
            
            $key   = $model->getKey();
            $index = $model->getIndex();
            $rs    = $curd->setElements([$index, $key])->select();
        } catch (Throwable) {
            $request->response->complete([], true);
            return;
        }
        
        $request->response->complete(
            array_map(fn($row) => [Key::LABEL => $row[$key], Key::VALUE => $row[$index]], $rs)
        );
    }   // 分页大小
    
    /**
     * ## 关联树选项  array<tree: array, keys: array>
     *
     * @param Request $request
     *
     * @noinspection PhpUnused
     */
    protected function area_action(Request $request): void
    {
        try {
            $request->response->complete(Texture::get($this->getModel())->asOptions());
        } catch (Throwable $e) {
            $request->response->complete(null, true, $e->getMessage());
        }
    }
    

    protected function upload_action(Request $request): void
    {
        $method = $request->getMethod();
        if ($method === 'OPTIONS') {
            $request->response->complete();
            return;
        }

        $request->response->render = Render::JSON;

        $path   = strtolower($request->section->short);

        $file = new Upload(__UPLOAD_DIR__ . strtr($path, '/', DIRECTORY_SEPARATOR));
        $name = $file->isError() ?: $file->url(__DOWNLOAD_URL__ . $path);

        $request->response->complete([ 'url' => $name ], $file->isError());
    }
    

    /**
     * ### 构造查询语句
     *
     * @param Request $request
     * @param array $view
     * @return array
     */
    private function _condition(Request $request, array $view = []): array
    {
        $pairs = $this->_query_url_pairs($request);
        $where = $view[self::WHERE] ?? [];
        $query = $this->_query_post_pairs($request);

        return [...$query, ...$where, ...$pairs];
    }

    private function _query_post_pairs(Request $request): array
    {
        $query = $request->getParam()[self::QUERY] ?? null;
        if (!$query) return [];

        $query = json_decode($query, true);
        return is_array($query) ? $query : [];
    }
    
    private function _query_url_pairs(Request $request): array
    {
        if (!($request instanceof Http)) return [];
        
        $pairs = $request->section->pairs;
        if ($pairs?->empty) return [];
        
        $r = [];
        foreach ($pairs as $k => $v) $r[] = [Key::NAME => $k, Key::VALUE => $v];
        return $r;
    }

    /**
     * ### 处理分页、排序设置
     *
     * @param Search $search
     * @param Request $request
     * @return array{ current: int, size: int }
     * @throws Exception
     */
    private function _addition(Search $search, Request $request): array
    {
        $option = $search->addition;
        $input  = $request->getParam();
        
        //分页
        $pagination = $input[self::PAGINATION] ?? [];
        $size       = $pagination[self::SIZE] ?? 10;
        $current    = $pagination[self::CURRENT] ?? 0;

        $return = [];
        if ($size > 0) {
            $option->page($current, $size);
            $return[self::CURRENT] = +$current;
            $return[self::SIZE]    = +$size;
        }
        
        //排序
        $this->_use_sort($option, $input);

        return $return;
    }

    private function _use_sort(Addition $option, array $input): void
    {
        $sorts = $input[self::SORT] ?? null;
        if (empty($sorts)) return;

        $sorts = json_decode($sorts, true);
        if (!is_array($sorts)) $sorts = [$sorts];

        try {
            $collection = $this->getModel();
        } catch (Throwable) { return; }

        array_walk(
            $sorts,
            function ($sort) use ($option, $collection) {
                [$name, $order] = (is_array($sort) ? $sort : [$sort, true/*desc*/]);
                try {
                    $col = $collection->getElement($name);
                    $option->orderBy($col->name, !$order);
                } catch (Throwable) {}
            },
        );
    }
    
//
//    private function _valid_admin(int | string | array $id, int $aid): bool
//    {
//        try {
//            $rows = $this->create_search()->setElements([Type::ID, Type::ADMIN])->setId($id)->select();
//            if (empty($rows) || !isset($rows[$id])) return false;
//            return $rows[$id][Type::ADMIN] === $aid;
//        }catch (Throwable) {}
//
//        return false;
//    }
//
//    /**
//     * 修改时
//     *
//     * @param int|string|array $id
//     * @param User             $user
//     *
//     * @return bool
//     */
//    private function _valid_department(int | string | array $id, User $user): bool
//    {
//        try {
//            $rows = $this->create_search()->setElements([Type::ID, Type::DEPARTMENT])->setId($id)->select();
//            if (empty($rows)) return false;
//            $row = array_pop($rows);
//            if (empty($row) || !isset($row[Type::DEPARTMENT])) return false;
//            return in_array($row[Type::DEPARTMENT], $user->departments);
//        }catch (Throwable $e) {
//            $this->recorder->Error($e);
//        }
//
//        return false;
//    }
//
    /**
     * 查询时自动附件用户和部门信息
     *
     * @param User  $user
     * @param array $condition
     *
     * @return array
     */
    protected function _fill_conditions(User $user, array $condition): array
    {
        if ($user->isAdministrator()) return $condition;

        /* TODO 当前用户 */
        if ($user->id === 0) {
            $condition[Type::OWNER] = $user->id;
            return $condition;
        }
        return $condition;
    }

    /**
     * 新增时自动附加 用户信息
     *
     * @param Http $http
     *
     * @return array
     */
    private function _add_column(Http $http): array
    {
        $input[Type::OWNER] = $http->user->id;
        return $input;
    }
}